package com.fsck.k9.voice;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;

import java.util.List;


import com.fsck.k9.voice.VoiceCommands;;


public class Voice extends AccessibilityService {
	
    /** Tag for logging from this service */
    private static final String LOG_TAG = "ClockBackService";

    // fields for configuring how the system handles this accessibility service

    /** Minimal timeout between accessibility events we want to receive */
    private static final int EVENT_NOTIFICATION_TIMEOUT_MILLIS = 80;

    /** Packages we are interested in */
    // This works with AlarmClock and Clock whose package name changes in different releases
    private static final String[] PACKAGE_NAMES = new String[] {
    	"com.fsck.k9", "com.fsck.k9.activity", "com.fsck.k9.activity.misc", "com.fsck.k9.activity.setup",
    	"com.fsck.k9.controller", "com.fsck.k9.crypto", "com.fsck.k9.helper", "com.fsck.k9.helper.power",
    	"com.fsck.k9.mail", "com.fsck.k9.mail.filter", "com.fsck.k9.mail.internet", "com.fsck.k9.store",
    	"com.fsck.k9.transport", "com.fsck.k9.transport.imap", "com.fsck.k9.preferences", "com.fsck.k9.provider",
    	"com.fsck.k9.remotecontrol", "com.fsck.k9.service", "com.fsck.k9.view", "com.fsck.k9.voice"
    };
	
    // message types we are passing around
    /** Speak */
    private static final int WHAT_SPEAK = 1;

    /** Stop speaking */
    private static final int WHAT_STOP_SPEAK = 2;

    /** Start the TTS service */
    private static final int WHAT_START_TTS = 3;

    /** Stop the TTS service */
    private static final int WHAT_SHUTDOWN_TTS = 4;

	
    /**
     * The queuing mode we are using - interrupt a spoken utterance before
     * speaking another one
     */
    private static final int QUEUING_MODE_INTERRUPT = 2;

    /** The empty string constant */
    private static final String SPACE = " ";

    // auxiliary fields

    /**
     * Handle to this service to enable inner classes to access the {@link Context}
     */
    private Context mContext;

    /** Reusable instance for building utterances */
    private final StringBuilder mUtterance = new StringBuilder();

    // feedback providing services

    /** The {@link TextToSpeech} used for speaking */
    private TextToSpeech mTts;

    /** Flag if the infrastructure is initialized */
    private boolean isInfrastructureInitialized;

    /** {@link Handler} for executing messages on the service main thread */
    Handler mHandler = new Handler() {
    
    	@Override
    	public void handleMessage(Message message) {
            switch (message.what) {
                case WHAT_SPEAK:
                    String utterance = (String) message.obj;
                    mTts.speak(utterance, QUEUING_MODE_INTERRUPT, null);
                    return;
                case WHAT_STOP_SPEAK:
                    mTts.stop();
                    return;
                case WHAT_START_TTS:
                    mTts = new TextToSpeech(mContext, null);
                    return;
                case WHAT_SHUTDOWN_TTS:
                    mTts.shutdown();
                    return;
            }
        }
    };
    
    @Override
    public void onServiceConnected() {
    	/** The system is telling us that it is bound to our service and we can configure 
    	 * ourselves - see setServiceInfo(int). Note that onBind() is final and cannot be 
    	 * Overridden.
    	 **/
        if (isInfrastructureInitialized) {
            return;
        }

        mContext = this;

        // send a message to start the TTS
        mHandler.sendEmptyMessage(WHAT_START_TTS);

        setServiceInfo(AccessibilityServiceInfo.FEEDBACK_SPOKEN);

        // we are in an initialized state now
        isInfrastructureInitialized = true;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        if (isInfrastructureInitialized) {
            // stop the TTS service
            mHandler.sendEmptyMessage(WHAT_SHUTDOWN_TTS);

            // we are not in an initialized state anymore
            isInfrastructureInitialized = false;
        }
        return false;
    }

    /**
     * Sets the {@link AccessibilityServiceInfo} which informs the system how to
     * handle this {@link AccessibilityService}.
     *
     * @param feedbackType The type of feedback this service will provide. </p>
     *            Note: The feedbackType parameter is an bitwise or of all
     *            feedback types this service would like to provide.
     */
    private void setServiceInfo(int feedbackType) {

	/** Here we are telling the system how to handle this accessibility service. 
	 * If setServiceInfo(AccessibilityServiceInfo) method is not called the system 
	 * will not deliver accessibility events to the service.
	 */

        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        // we are interested in all types of accessibility events
        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        // we want to provide specific type of feedback
        info.feedbackType = feedbackType;
        // we want to receive events in a certain interval
        info.notificationTimeout = EVENT_NOTIFICATION_TIMEOUT_MILLIS;
        // we want to receive accessibility events only from certain packages
        info.packageNames = PACKAGE_NAMES;
        setServiceInfo(info);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.i(LOG_TAG, event.toString());

        mHandler.obtainMessage(WHAT_SPEAK, formatUtterance(event)).sendToTarget();
    }

    @Override
    public void onInterrupt() {
        mHandler.obtainMessage(WHAT_STOP_SPEAK);
    }

    /**
     * Formats an utterance from an {@link AccessibilityEvent}.
     *
     * @param event The event from which to format an utterance.
     * @return The formatted utterance.
     */
    private String formatUtterance(AccessibilityEvent event) {
        StringBuilder utterance = mUtterance;

        // clear the utterance before appending the formatted text
        utterance.delete(0, utterance.length());

        List<CharSequence> eventText = event.getText();

        // We try to get the event text if such
        /** Build an utterance form the event text. **/

        if (!eventText.isEmpty()) {
            for (CharSequence subText : eventText) {
                utterance.append(subText);
                utterance.append(SPACE);
            }

            return utterance.toString();
        }

        // There is no event text but we try to get the content description which is
        // an optional attribute for describing a view (typically used with ImageView)
        CharSequence contentDescription = event.getContentDescription();
        /** If the event has no text we fall-back to the content description.The 
         * contentDescription attribute can be applied to all Views and provides information 
         * about "What is on the View". This is especially useful for image views so an 
         * accessibility service can provide spoken feedback about the content of the image.
         **/ 

        if (contentDescription != null) {
            utterance.append(contentDescription);
            return utterance.toString();
        }

        return utterance.toString();
    }
}